using AnimCollections;

namespace DA.AnimGraph {
    internal static class NodeConstructure {
        public static unsafe void New<T>(out NodeLink* link, out T* node, ref GraphBuffer buffer, int childrenCount) where T : unmanaged, INode {
            link = buffer.Malloc<NodeLink>();
            *link = new();

            node = buffer.Malloc<T>();
            *node = new();

            link->nodeType = AGRuntimeTypeRegister.GetInstance().GetEnumByType(typeof(T));

            if (childrenCount > 0) {
                buffer.MallocNativeSimpleArray(out link->children, childrenCount);
                buffer.MallocNativeSimpleArray(out link->weights, childrenCount * 2);
            } else {
                link->children = default;
                link->weights = default;
            }
        }
    }

    public unsafe partial struct ClipNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out ClipNode* node, ref buffer, 0);
            link->nodeFlag |= NodeFlag.Sample;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }

        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.ClipNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.ApplyFunction = Apply;
            return vt;
        }
    }

    public unsafe partial struct SimpleBlendNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out SimpleBlendNode* node, ref buffer, 2);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }

        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.SimpleBlendNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            return vt;
        }
    }

    public unsafe partial struct BlendSpace2DNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out BlendSpace2DNode* node, ref buffer, 0);
            link->nodeFlag |= NodeFlag.Sample;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }

        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.BlendSpace2DNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.ApplyFunction = Apply;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }

    public unsafe partial struct BlendSpace1DNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out BlendSpace1DNode* node, ref buffer, 0);
            link->nodeFlag |= NodeFlag.Sample;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.BlendSpace1DNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.ApplyFunction = Apply;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }

    public unsafe partial struct StateMachineNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, int childrenCount, in SetupData data) {
            NodeConstructure.New(out link, out StateMachineNode* node, ref buffer, childrenCount);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.StateMachineNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }

    public unsafe partial struct LayerBlendNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, int childrenCount, in SetupData data) {
            NodeConstructure.New(out link, out LayerBlendNode* node, ref buffer, childrenCount);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.LayerBlendNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.PostEvaluateFunction = PostEvaluate;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }

    public unsafe partial struct SelectNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, int childrenCount, in SetupData data) {
            NodeConstructure.New(out link, out SelectNode* node, ref buffer, childrenCount);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.SelectNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.DisposeFunction = Dispose;
            vt.GetTimeStateFunction = GetTimeState;
            return vt;
        }
    }

    public unsafe partial struct RevolveNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, int childrenCount, in SetupData data) {
            NodeConstructure.New(out link, out RevolveNode* node, ref buffer, childrenCount);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.RevolveNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.DisposeFunction = Dispose;
            vt.GetTimeStateFunction = GetTimeState;
            return vt;
        }
    }

    public unsafe partial struct AxisBlend1DNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, int childrenCount, in SetupData data) {
            NodeConstructure.New(out link, out AxisBlend1DNode* node, ref buffer, childrenCount);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.AxisBlend1DNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }

    public unsafe partial struct SlotNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out SlotNode* node, ref buffer, 2);
            link->nodeFlag |= NodeFlag.Blend;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.SlotNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.InitFunction = Init;
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.DisposeFunction = Dispose;
            vt.GetTimeStateFunction = GetTimeState;
            return vt;
        }
    }

    public unsafe partial struct SequenceNode {
        public static NodeLink* New(out NodeLink* link, ref GraphBuffer buffer, in SetupData data) {
            NodeConstructure.New(out link, out SequenceNode* node, ref buffer, 0);
            link->nodeFlag |= NodeFlag.Sample;
            SetupFunction(link);
            link->SetupNode(node);
            node->Setup(data, ref buffer, link);
            return link;
        }
        private static void SetupFunction(NodeLink* link) {
            link->virtualFunction = AGRuntimeTypeRegister.GetInstance().GetNodeVirtualTable(AGRuntimeType.SequenceNode);
        }
        public static NodeVirtualTable GetVirtualFunction() {
            var vt = new NodeVirtualTable();
            vt.BuildPlayableGraphFunction = &BuildPlayableGraph;
            vt.EvaluateFunction = Evaluate;
            vt.ApplyFunction = Apply;
            vt.DisposeFunction = Dispose;
            return vt;
        }
    }
}
